package com.supreme.studyserver.service;

import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class ThreadJucService {

    static class MyTest {
        public volatile int number = 0;
        public void changeNumber(){
            number = 100;
        }
    }

    /**
     * Thread volatile
     */
    public void volatileThreadTest() {
        MyTest myTest = new MyTest();

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.printf("线程%s开始执行%n", Thread.currentThread().getName());

                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                myTest.changeNumber();
                System.out.printf("线程%s的number：%d%n", Thread.currentThread().getName(), myTest.number);
            }
        });
        thread1.start();

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.printf("线程%s开始执行%n", Thread.currentThread().getName());

                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.printf("线程%s的number：%d%n", Thread.currentThread().getName(), myTest.number);
            }
        });
        thread2.start();

        while (myTest.number == 0) {

        }
        System.out.println("执行完毕");
    }

    static class MyTest2 {
        public volatile int number = 0;
        public synchronized void incr(){
            number++;
        }
    }

    /**
     * Thread + volatile + synchronized
     */
    public void volatileThreadTest2() {
        MyTest2 myTest = new MyTest2();
        for (int i = 1; i <= 10; i++){
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++){
                    myTest.incr();
                }
            }, "Thread" + i).start();
        }

        //等线程执行结束了，输出number值
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println("当前number：" + myTest.number);
    }

    static class MyTest3 {
        public volatile AtomicInteger number = new AtomicInteger();
        public void incr(){
            number.getAndIncrement();
        }
    }

    /**
     * Thread + volatile + AtomicInteger
     */
    public void volatileThreadTest3() {
        MyTest3 myTest = new MyTest3();
        for (int i = 1; i <= 10; i++){
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++){
                    myTest.incr();
                }
            }, "Thread" + i).start();
        }

        //等线程执行结束了，输出number值
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("当前number：" + myTest.number);
    }

    private static final ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "globalLocal");

    /**
     * Thread + ThreadLocal
     */
    public void threadLocalTest() {
        final String mainLocal1 = threadLocal.get();
        System.out.println("threadLocalTest mainLocal1: " + mainLocal1);
        threadLocal.set("mainHello");

        final Thread thread1 = new Thread(() -> {
            final String threadLocalVal1 = threadLocal.get();
            System.out.println("thread1: " + threadLocalVal1);

            threadLocal.set("thread1Hello");
            final String threadLocalVal2 = threadLocal.get();
            System.out.println("thread1: " + threadLocalVal2);
            threadLocal.remove();
        });
        thread1.start();
        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        final Thread thread2 = new Thread(() -> {
            final String thread2Local = threadLocal.get();
            System.out.println("thread2: " + thread2Local);
            threadLocal.remove();
        });
        thread2.start();
        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        final String mainLocal2 = threadLocal.get();
        threadLocal.remove();
        System.out.println("threadLocalTest mainLocal2: " + mainLocal2);
    }

    public void debugThread(Integer loopCount) {
        List<char[]> charsList = new ArrayList<>();

        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < loopCount; i++) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(500);
                        charsList.add(new char[1024 * 1024]);
                        System.out.println("debugThread: " + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        thread.start();
        System.out.println("debugThread finish");
    }

    private static final Lock lock1 = new ReentrantLock();
    private static final Lock lock2 = new ReentrantLock();

    public void demoDeadLockThread() {
        Thread t1 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " start");
                lock1.lock();
                TimeUnit.SECONDS.sleep(1);
                lock2.lock();
                System.out.println(Thread.currentThread().getName() + " finish");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " start");
                lock2.lock();
                TimeUnit.SECONDS.sleep(1);
                lock1.lock();
                System.out.println(Thread.currentThread().getName() + " finish");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.setName("demoThread1");
        t2.setName("demoThread2");
        t1.start();
        t2.start();

        System.out.println("demoDeadLockThread finish");
    }

    public void releaseThreadLock() {
        lock1.unlock();
        lock2.unlock();
    }
}
